var _ctRomanMap = [ [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'] ]; function _ctDecimalToRoman(n) { if (!isFinite(n) || n < 1 || n > 3999 || Math.floor(n) !== n) return null; var out = ''; for (var i = 0; i < _ctRomanMap.length; i++) { while (n >= _ctRomanMap[i][0]) { out += _ctRomanMap[i][1]; n -= _ctRomanMap[i][0]; } } return out; } function _ctRomanToDecimal(s) { var v = (s || '').toUpperCase().trim(); if (!v || !/^[MDCLXVI]+$/.test(v)) return null; var values = { I:1, V:5, X:10, L:50, C:100, D:500, M:1000 }; var total = 0; for (var i = 0; i < v.length; i++) { var cur = values[v.charAt(i)]; var next = values[v.charAt(i + 1)]; if (next && cur < next) { total += next - cur; i++; } else { total += cur; } } // Re-encode to validate (rejects malformed Roman like IIII or VV). var canonical = _ctDecimalToRoman(total); if (canonical !== v) return null; return total; } function convert(input) { var s = (input || '').trim(); if (!s) return ''; if (/^-?\d+$/.test(s)) { var n = parseInt(s, 10); var roman = _ctDecimalToRoman(n); if (roman == null) return 'Roman numerals only support integers from 1 to 3999.'; return n + ' = ' + roman; } var dec = _ctRomanToDecimal(s); if (dec == null) return 'That is not a valid Roman numeral.'; return s.toUpperCase() + ' = ' + dec; } var _loadedScripts = {}; function loadScriptPromise(url) { if (_loadedScripts[url]) return _loadedScripts[url]; _loadedScripts[url] = new Promise(function (resolve, reject) { var s = document.createElement('script'); s.src = url; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); return _loadedScripts[url]; } function replaceAll(find, replace, str) { return str.replace(new RegExp(find, 'g'), replace); } function beautify(str) { var result = ''; var length = str.length; var i = 0; var braceCountLeft = 0; var braceCountRight = 0; var withinQuotes = false; while (i < length) { var c = str[i]; if (c == '"' && (i == 0 || c[i - 1] != '\\')) { // non-escaped quotes withinQuotes = !withinQuotes; } if (!withinQuotes && (c == '}' || c == '{' || c == ',')) { console.log('Start####' + result); // look back and remove carriage returns and whitespace that are already there var resultIndex = result.length - 1; while (resultIndex >= 0 && (result[resultIndex] == ' ' || result[resultIndex] == '\r' || result[resultIndex] == '\n' || result[resultIndex] == '\t')) { resultIndex = resultIndex - 1; result = result.substr(0, resultIndex + 1); console.log('char ' + result[resultIndex] + '-----' + result + 'zzz ' + result.length + ' ' + resultIndex); } if (c == '{') { braceCountLeft++; result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } else if (c == '}') { braceCountRight++; // precede with carriage return result += '\r' + GetTabs(braceCountLeft - braceCountRight) + c; } else if (c == ',') { result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } var nextChar = ''; // advance through whitespace and remove carriage returns that are already there while (i < length && (str[i + 1] == ' ' || str[i + 1] == '\r' || str[i + 1] == '\n' || str[i + 1] == '\t')) { i++; } } else { result += str[i]; } i++; } return result; } function GetTabs(count) { var result = ''; for (var i = 0; i < count; i++) { result += ' '; } return result; }